<!DOCTYPE html>
<html><head>
<title>用Unity3D实现【牧师与魔鬼过河】</title>




<meta charset="utf-8">
<meta name="X-UA-Compatible" content="IE=edge">
<meta name="google-site-verification" content="">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<meta content="telephone=no" name="format-detection">
<meta name="description" content="">
<meta name="renderer" content="webkit">
<meta name="theme-color" content="#ffffff">



<meta property="og:title" content="用Unity3D实现【牧师与魔鬼过河】" />
<meta property="og:description" content="用Unity3D实现【牧师与魔鬼过河】  相关链接 项目仓库：https://gitee.com/WondrousWisdomcard/unity3d-homework
项目文档：https://gitee.com/WondrousWisdomcard/unity3d-homework/blob/master/Homework02/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3.md
演示视频：https://www.bilibili.com/video/BV1Nf4y177wF?spm_id_from=333.999.0.0
 游戏规则  有三个牧师和三个魔鬼在河的一岸边等待过河 船一次最多搭载两人，最少搭载一人 一旦有一岸边有牧师，且牧师的数量少于魔鬼的数量，魔鬼就会把牧师吃掉，游戏失败 玩家需要使用策略让所有牧师和魔鬼成功到达河的对岸     游戏失败 游戏成功          MVC架构 MVC架构中的M、V、C分别指Model模型、View视图、Controller控制器，模型负责管理的就是游戏中个对象，View负责管理与玩家的交互，如点击游戏对象、点击重启按钮等，而控制器如其名，控制模型和视图，例如接收模型层的计算结果，再将其作为输入传入视图层以显示给玩家。
设计细节 第一步：分析游戏对象 在设计之初，我们首先需要分析游戏中有哪些对象：“牧师、魔鬼、船、河岸”还不够清晰，我们可以加入量词：“三个牧师、三个魔鬼、一艘船、两边河岸”，但还不够，还需要向三个牧师之间有没有区别，以此类推，例如河岸就有起点河岸和重点河岸，它们显然不同，而牧师之间和魔鬼之间，暂时还没有区别，所以最后的我们分析出的对象是：
三个牧师、三个魔鬼、一艘船、起点河岸和终点河岸。
 Tip: 分析游戏对象是在游戏设计前必须做的一件事，因为我们后续设计的模型类、控制器类和它们密切相关，如果没有好好去分析对象、分析对象数目、分析同类对象之间有没有差别，在之后的设计中可能会导致混乱。因此，分析对象可以用一个简单的三步走方法：“有什么对象” - “对象各有几个” - “同类对象之间有没有差别”。
 第二步：分析玩家游戏行为 游戏行为与游戏规则密切相关，但往往一个游戏中游戏行为不会很多，我们可以使用一个表列出来。在我们分析好游戏行为后，将使用Fasade门面模式来实现交互行为的设计。
我们可以发现，牧师与魔鬼过河中的游戏行为有四个：点击牧师、点击魔鬼、点击船、重新开始游戏，我们发现点击牧师和点击魔鬼可以用点击角色来替换，因为二者的游戏行为本质上没有差别，再加上游戏行为少往往能让代码更加简短清晰，随意我们最终总结出三个游戏行为，如下表格所示：
   动作 参数 结果     重启游戏 gameRestart  所有游戏对象回到初始状态   点击牧师/魔鬼 clickCharacter 指定角色 若在岸上则上船，若在船上则上岸   点击船 clickBoat  在有人在船上的前提下，移动到对岸    第三步：实现 我们使用MVC架构实现游戏。以下是MVC架构的UML类图模板，以GUI为后缀的类属于视图层、以Controller为后缀属于控制器层，以Model为后缀的类属于模型层，在接下来的类的设计中我们也遵循这样的命名规则。" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://wondrouswisdomcard.gitee.io/posts/unity3d2/" />
<meta property="article:published_time" content="2021-10-07T00:00:00+00:00" />
<meta property="article:modified_time" content="2021-10-07T00:00:00+00:00" /><meta property="og:site_name" content="My Blog" />





<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="用Unity3D实现【牧师与魔鬼过河】"/>
<meta name="twitter:description" content="用Unity3D实现【牧师与魔鬼过河】  相关链接 项目仓库：https://gitee.com/WondrousWisdomcard/unity3d-homework
项目文档：https://gitee.com/WondrousWisdomcard/unity3d-homework/blob/master/Homework02/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3.md
演示视频：https://www.bilibili.com/video/BV1Nf4y177wF?spm_id_from=333.999.0.0
 游戏规则  有三个牧师和三个魔鬼在河的一岸边等待过河 船一次最多搭载两人，最少搭载一人 一旦有一岸边有牧师，且牧师的数量少于魔鬼的数量，魔鬼就会把牧师吃掉，游戏失败 玩家需要使用策略让所有牧师和魔鬼成功到达河的对岸     游戏失败 游戏成功          MVC架构 MVC架构中的M、V、C分别指Model模型、View视图、Controller控制器，模型负责管理的就是游戏中个对象，View负责管理与玩家的交互，如点击游戏对象、点击重启按钮等，而控制器如其名，控制模型和视图，例如接收模型层的计算结果，再将其作为输入传入视图层以显示给玩家。
设计细节 第一步：分析游戏对象 在设计之初，我们首先需要分析游戏中有哪些对象：“牧师、魔鬼、船、河岸”还不够清晰，我们可以加入量词：“三个牧师、三个魔鬼、一艘船、两边河岸”，但还不够，还需要向三个牧师之间有没有区别，以此类推，例如河岸就有起点河岸和重点河岸，它们显然不同，而牧师之间和魔鬼之间，暂时还没有区别，所以最后的我们分析出的对象是：
三个牧师、三个魔鬼、一艘船、起点河岸和终点河岸。
 Tip: 分析游戏对象是在游戏设计前必须做的一件事，因为我们后续设计的模型类、控制器类和它们密切相关，如果没有好好去分析对象、分析对象数目、分析同类对象之间有没有差别，在之后的设计中可能会导致混乱。因此，分析对象可以用一个简单的三步走方法：“有什么对象” - “对象各有几个” - “同类对象之间有没有差别”。
 第二步：分析玩家游戏行为 游戏行为与游戏规则密切相关，但往往一个游戏中游戏行为不会很多，我们可以使用一个表列出来。在我们分析好游戏行为后，将使用Fasade门面模式来实现交互行为的设计。
我们可以发现，牧师与魔鬼过河中的游戏行为有四个：点击牧师、点击魔鬼、点击船、重新开始游戏，我们发现点击牧师和点击魔鬼可以用点击角色来替换，因为二者的游戏行为本质上没有差别，再加上游戏行为少往往能让代码更加简短清晰，随意我们最终总结出三个游戏行为，如下表格所示：
   动作 参数 结果     重启游戏 gameRestart  所有游戏对象回到初始状态   点击牧师/魔鬼 clickCharacter 指定角色 若在岸上则上船，若在船上则上岸   点击船 clickBoat  在有人在船上的前提下，移动到对岸    第三步：实现 我们使用MVC架构实现游戏。以下是MVC架构的UML类图模板，以GUI为后缀的类属于视图层、以Controller为后缀属于控制器层，以Model为后缀的类属于模型层，在接下来的类的设计中我们也遵循这样的命名规则。"/>







<script src="/vendor/js/jquery.min.js" ></script>
<script src="/vendor/js/popper.min.js" ></script>
<script src="/vendor/js/bootstrap.min.js" ></script>
<script src="/vendor/js/smooth-scroll.polyfills.min.js" ></script>
<link type="text/css" rel="stylesheet" href="/vendor/css/bootstrap.min.css">
<script src="/vendor/js/vue.min.js" ></script>






<link rel="stylesheet" href="https://wondrouswisdomcard.gitee.io/scss/journal.min.c116bc90d171283f099f173854157ec8f183f9073b93443b2c8ad82899ee9025.css" integrity="sha256-wRa8kNFxKD8Jnxc4VBV&#43;yPGD&#43;Qc7k0Q7LIrYKJnukCU=" media="screen">



<link rel="stylesheet" href="https://wondrouswisdomcard.gitee.io/scss/dark-mode.min.552aae4638a84aa57cf0b195750a49ea9131d3bb621d1ed3ebc9b14b18166536.css" integrity="sha256-VSquRjioSqV88LGVdQpJ6pEx07tiHR7T68mxSxgWZTY=" media="screen">


<script src="https://wondrouswisdomcard.gitee.io/js/loadCSS.js"></script>
<script>
  loadCSS("https://fonts.googleapis.com/css?family=Lora|Montserrat|Fira+Mono|Noto+Serif+SC|Material+Icons");
</script>




  
    <script src="https://wondrouswisdomcard.gitee.io/js/toc-collapse.js"></script>
  



<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script src="/vendor/js/md5.min.js"></script>
<script>
  var gitalk = new Gitalk({
  clientID: 'your client id',
  clientSecret: 'your client secret',
  repo: 'repo name',
  owner: 'user',
  admin: ['user'],
  id: md5(location.pathname),
  distractionFreeMode: 'false'
  });
  window.onload = function () {
        gitalk.render('gitalk-container')
  }
</script>







<script src="https://cdn.jsdelivr.net/npm/vue-disqus@3/dist/vue-disqus.js"></script>


</head>
<body>
    	<div id="app"><div ref="sideContainer" class="side-container">
    
    <a class="a-block nav-head false" href="https://wondrouswisdomcard.gitee.io">
    
        <div class="nav-title">
            Wondrous WindomCard&#39;s Blog
        </div>
        
        <div class="nav-subtitle">
            Themed by Diary.
        </div>
        
    </a>

    <div class="nav-link-list">
        
        
            
            
            
                
            
            
            
            <a class="a-block nav-link-item active" href="/posts">
                Archive
            </a>
            
        
            
            
            
            
            
            <a class="a-block nav-link-item false" href="/categories">
                Categories
            </a>
            
        
            
            
            
            
            
            <a class="a-block nav-link-item false" href="/tags">
                Tags
            </a>
            
        
            
            
            
            
            
            <a class="a-block nav-link-item false" href="/index.xml">
                RSS Feed
            </a>
            
        
    </div>

    

    <div class="nav-footer">
        
Hugo Theme <a href="https://github.com/amazingrise/hugo-theme-diary">Diary</a> by <a href="https://amazingrise.net">Rise</a>
<br>
Ported from <a href="https://mak1t0.cc/" target="_blank" rel="noreferrer noopener">Makito</a>'s <a href="https://github.com/SumiMakito/hexo-theme-journal/" target="_blank" rel="noreferrer noopener">Journal.</a> <br>
<br>

&copy;
	
	This is a customized copyright.
	

    </div>
    
</div><div ref="extraContainer" class="extra-container">
    
    
    <div class="toc animated-visibility" :class="{ invisible: scrollY <= 140 }">


	<div class="toc-content">
	
		
		
		
		<center>- CATALOG -</center>
		
		
		<ul>
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e7%94%a8unity3d%e5%ae%9e%e7%8e%b0%e7%89%a7%e5%b8%88%e4%b8%8e%e9%ad%94%e9%ac%bc%e8%bf%87%e6%b2%b3" onclick="onNavClick(`#用unity3d实现牧师与魔鬼过河-nav`)" id="用unity3d实现牧师与魔鬼过河-nav">
									用Unity3D实现【牧师与魔鬼过河】
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
							
								<ul class="collapse" data-toggle="collapse">
							
						
						
							<li>
								<a href="#%e7%9b%b8%e5%85%b3%e9%93%be%e6%8e%a5" onclick="onNavClick(`#相关链接-nav`)" id="相关链接-nav">
									相关链接
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e6%b8%b8%e6%88%8f%e8%a7%84%e5%88%99" onclick="onNavClick(`#游戏规则-nav`)" id="游戏规则-nav">
									游戏规则
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#mvc%e6%9e%b6%e6%9e%84" onclick="onNavClick(`#mvc架构-nav`)" id="mvc架构-nav">
									MVC架构
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e8%ae%be%e8%ae%a1%e7%bb%86%e8%8a%82" onclick="onNavClick(`#设计细节-nav`)" id="设计细节-nav">
									设计细节
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
							
								<ul class="collapse" data-toggle="collapse">
							
						
						
							<li>
								<a href="#%e7%ac%ac%e4%b8%80%e6%ad%a5%e5%88%86%e6%9e%90%e6%b8%b8%e6%88%8f%e5%af%b9%e8%b1%a1" onclick="onNavClick(`#第一步分析游戏对象-nav`)" id="第一步分析游戏对象-nav">
									第一步：分析游戏对象
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e5%88%86%e6%9e%90%e7%8e%a9%e5%ae%b6%e6%b8%b8%e6%88%8f%e8%a1%8c%e4%b8%ba" onclick="onNavClick(`#第二步分析玩家游戏行为-nav`)" id="第二步分析玩家游戏行为-nav">
									第二步：分析玩家游戏行为
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e7%ac%ac%e4%b8%89%e6%ad%a5%e5%ae%9e%e7%8e%b0" onclick="onNavClick(`#第三步实现-nav`)" id="第三步实现-nav">
									第三步：实现
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
							
								<ul class="collapse" data-toggle="collapse">
							
						
						
							<li>
								<a href="#director%e7%b1%bb" onclick="onNavClick(`#director类-nav`)" id="director类-nav">
									Director类
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#iscenecontroller%e6%8e%a5%e5%8f%a3" onclick="onNavClick(`#iscenecontroller接口-nav`)" id="iscenecontroller接口-nav">
									ISceneController接口
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#iuseraction" onclick="onNavClick(`#iuseraction-nav`)" id="iuseraction-nav">
									IUserAction
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#moveable%e7%b1%bb" onclick="onNavClick(`#moveable类-nav`)" id="moveable类-nav">
									Moveable类
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#charactermodel" onclick="onNavClick(`#charactermodel-nav`)" id="charactermodel-nav">
									CharacterModel
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#boatmodel" onclick="onNavClick(`#boatmodel-nav`)" id="boatmodel-nav">
									BoatModel
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#usergui" onclick="onNavClick(`#usergui-nav`)" id="usergui-nav">
									UserGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#boatgui" onclick="onNavClick(`#boatgui-nav`)" id="boatgui-nav">
									BoatGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#charactergui" onclick="onNavClick(`#charactergui-nav`)" id="charactergui-nav">
									CharacterGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#scenegui" onclick="onNavClick(`#scenegui-nav`)" id="scenegui-nav">
									SceneGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#mainscenecontroller" onclick="onNavClick(`#mainscenecontroller-nav`)" id="mainscenecontroller-nav">
									MainSceneController
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
							
								</ul>
							
						
						
						
							<li>
								<a href="#uml" onclick="onNavClick(`#uml-nav`)" id="uml-nav">
									UML
								</a>
							</li>
						
						
					
				
			
		</ul>
	</div>

</div>
    
    <div class="pagination">
        <a id="globalBackToTop" class="pagination-action animated-visibility" href="#top" :class="{ invisible: scrollY == 0 }">
            <i class="material-icons pagination-action-icon">
                keyboard_arrow_up
            </i>
        </a>
        
        <a class="pagination-action" v-on:click="toggleDarkMode">
            <i class="material-icons pagination-action-icon" v-if="isDarkMode">
                brightness_4
            </i>
            <i class="material-icons pagination-action-icon" v-else="isDarkMode">
                brightness_7
            </i>
        </a>
        
        
    </div>
</div>
<div class="single-column-drawer-container" ref="drawer"
     v-bind:class="{ 'single-column-drawer-container-active': isDrawerOpen }">
    <div class="drawer-content">
        <div class="drawer-menu">
            
            
            
                
                
                
                    
                
                
                
                <a class="a-block drawer-menu-item active" href="/posts">
                    Archive
                </a>
                
            
                
                
                
                
                
                <a class="a-block drawer-menu-item false" href="/categories">
                    Categories
                </a>
                
            
                
                
                
                
                
                <a class="a-block drawer-menu-item false" href="/tags">
                    Tags
                </a>
                
            
                
                
                
                
                
                <a class="a-block drawer-menu-item false" href="/index.xml">
                    RSS Feed
                </a>
                
            
            
            <div class="toc">


	<div class="toc-content">
	
		
		
		
		<center>- CATALOG -</center>
		
		
		<ul>
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e7%94%a8unity3d%e5%ae%9e%e7%8e%b0%e7%89%a7%e5%b8%88%e4%b8%8e%e9%ad%94%e9%ac%bc%e8%bf%87%e6%b2%b3" onclick="onNavClick(`#用unity3d实现牧师与魔鬼过河-nav`)" id="用unity3d实现牧师与魔鬼过河-nav">
									用Unity3D实现【牧师与魔鬼过河】
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
							
								<ul class="collapse" data-toggle="collapse">
							
						
						
							<li>
								<a href="#%e7%9b%b8%e5%85%b3%e9%93%be%e6%8e%a5" onclick="onNavClick(`#相关链接-nav`)" id="相关链接-nav">
									相关链接
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e6%b8%b8%e6%88%8f%e8%a7%84%e5%88%99" onclick="onNavClick(`#游戏规则-nav`)" id="游戏规则-nav">
									游戏规则
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#mvc%e6%9e%b6%e6%9e%84" onclick="onNavClick(`#mvc架构-nav`)" id="mvc架构-nav">
									MVC架构
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e8%ae%be%e8%ae%a1%e7%bb%86%e8%8a%82" onclick="onNavClick(`#设计细节-nav`)" id="设计细节-nav">
									设计细节
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
							
								<ul class="collapse" data-toggle="collapse">
							
						
						
							<li>
								<a href="#%e7%ac%ac%e4%b8%80%e6%ad%a5%e5%88%86%e6%9e%90%e6%b8%b8%e6%88%8f%e5%af%b9%e8%b1%a1" onclick="onNavClick(`#第一步分析游戏对象-nav`)" id="第一步分析游戏对象-nav">
									第一步：分析游戏对象
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e5%88%86%e6%9e%90%e7%8e%a9%e5%ae%b6%e6%b8%b8%e6%88%8f%e8%a1%8c%e4%b8%ba" onclick="onNavClick(`#第二步分析玩家游戏行为-nav`)" id="第二步分析玩家游戏行为-nav">
									第二步：分析玩家游戏行为
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#%e7%ac%ac%e4%b8%89%e6%ad%a5%e5%ae%9e%e7%8e%b0" onclick="onNavClick(`#第三步实现-nav`)" id="第三步实现-nav">
									第三步：实现
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
							
								<ul class="collapse" data-toggle="collapse">
							
						
						
							<li>
								<a href="#director%e7%b1%bb" onclick="onNavClick(`#director类-nav`)" id="director类-nav">
									Director类
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#iscenecontroller%e6%8e%a5%e5%8f%a3" onclick="onNavClick(`#iscenecontroller接口-nav`)" id="iscenecontroller接口-nav">
									ISceneController接口
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#iuseraction" onclick="onNavClick(`#iuseraction-nav`)" id="iuseraction-nav">
									IUserAction
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#moveable%e7%b1%bb" onclick="onNavClick(`#moveable类-nav`)" id="moveable类-nav">
									Moveable类
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#charactermodel" onclick="onNavClick(`#charactermodel-nav`)" id="charactermodel-nav">
									CharacterModel
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#boatmodel" onclick="onNavClick(`#boatmodel-nav`)" id="boatmodel-nav">
									BoatModel
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#usergui" onclick="onNavClick(`#usergui-nav`)" id="usergui-nav">
									UserGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#boatgui" onclick="onNavClick(`#boatgui-nav`)" id="boatgui-nav">
									BoatGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#charactergui" onclick="onNavClick(`#charactergui-nav`)" id="charactergui-nav">
									CharacterGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#scenegui" onclick="onNavClick(`#scenegui-nav`)" id="scenegui-nav">
									SceneGUI
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
						
						
							<li>
								<a href="#mainscenecontroller" onclick="onNavClick(`#mainscenecontroller-nav`)" id="mainscenecontroller-nav">
									MainSceneController
								</a>
							</li>
						
						
					
				
			
				
				
					
						
						
						
							
								</ul>
							
						
						
						
							<li>
								<a href="#uml" onclick="onNavClick(`#uml-nav`)" id="uml-nav">
									UML
								</a>
							</li>
						
						
					
				
			
		</ul>
	</div>

</div>
            
        </div>
    </div>
</div>
<transition name="fade">
    <div v-bind:class="{ 'single-column-drawer-mask': mounted }" v-if="isDrawerOpen" v-on:click="toggleDrawer"></div>
</transition>
<nav ref="navBar" class="navbar sticky-top navbar-light single-column-nav-container">
    <div ref="navBackground" class="nav-background"></div>
    <div class="container container-narrow nav-content">
        <button id="nav_dropdown_btn" class="nav-dropdown-toggle" type="button" v-on:click="toggleDrawer">
            <i class="material-icons">
                menu
            </i>
        </button>
        <a ref="navTitle" class="navbar-brand" href="https://wondrouswisdomcard.gitee.io">
            Wondrous WindomCard&#39;s Blog
        </a>
        
        <button type="button" class="nav-darkmode-toggle" v-on:click="toggleDarkMode">
            <i class="material-icons" v-if="isDarkMode">
                brightness_4
            </i>
            <i class="material-icons" v-else="isDarkMode">
                brightness_7
            </i>
        </button>
        
    </div>
</nav>
<div class="single-column-header-container" ref="pageHead"
     v-bind:style="{ transform: 'translateZ(0px) translateY('+.3*scrollY+'px)', opacity: 1-navOpacity }">
    <a href="https://wondrouswisdomcard.gitee.io">
        <div class="single-column-header-title">Wondrous WindomCard&#39;s Blog</div>
        
        <div class="single-column-header-subtitle">Themed by Diary.</div>
        

    </a>
</div>

            <div id="content">
<div ref="streamContainer" class="stream-container">
    <div class="post-list-container post-list-container-shadow">
        <div class="post">
            
            
            

            <div class="post-head-wrapper-text-only"
                
            >
                <div class="post-title">
                    用Unity3D实现【牧师与魔鬼过河】
                    
                    <div class="post-meta">
                        
                        <time itemprop="datePublished">
                            2021-10-07 00:00
                        </time>
                        

                        
                            <i class="material-icons" style="">folder</i>
                                <a href="/categories/">[Unity3D]</a>
                                &nbsp;
                        

                        
                            <i class="material-icons" style="">label</i>
                            
                                <a href="/tags/unity3d">Unity3D</a>
                                &nbsp;
                            
                        
                        
                    </div>
                </div>
            </div>
            
            <div class="post-body-wrapper">
                
                <div class="post-body" v-pre>
                
                    <h1 id="用unity3d实现牧师与魔鬼过河">用Unity3D实现【牧师与魔鬼过河】</h1>
<hr>
<h2 id="相关链接">相关链接</h2>
<p>项目仓库：https://gitee.com/WondrousWisdomcard/unity3d-homework</p>
<p>项目文档：https://gitee.com/WondrousWisdomcard/unity3d-homework/blob/master/Homework02/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3.md</p>
<p>演示视频：https://www.bilibili.com/video/BV1Nf4y177wF?spm_id_from=333.999.0.0</p>
<hr>
<h2 id="游戏规则">游戏规则</h2>
<ul>
<li>有三个牧师和三个魔鬼在河的一岸边等待过河</li>
<li>船一次最多搭载两人，最少搭载一人</li>
<li>一旦有一岸边有牧师，且牧师的数量少于魔鬼的数量，魔鬼就会把牧师吃掉，游戏失败</li>
<li>玩家需要使用策略让所有牧师和魔鬼成功到达河的对岸</li>
</ul>
<p><img src="../../images/unity3d2/1.png" alt="1"></p>
<table>
<thead>
<tr>
<th>游戏失败</th>
<th>游戏成功</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="../../images/unity3d2/2.png" alt="2"></td>
<td><img src="../../images/unity3d2/3.png" alt="3"></td>
</tr>
</tbody>
</table>
<h2 id="mvc架构">MVC架构</h2>
<p>MVC架构中的M、V、C分别指Model模型、View视图、Controller控制器，模型负责管理的就是游戏中个对象，View负责管理与玩家的交互，如点击游戏对象、点击重启按钮等，而控制器如其名，控制模型和视图，例如接收模型层的计算结果，再将其作为输入传入视图层以显示给玩家。</p>
<h2 id="设计细节">设计细节</h2>
<h3 id="第一步分析游戏对象">第一步：分析游戏对象</h3>
<p>在设计之初，我们首先需要分析游戏中有哪些对象：“牧师、魔鬼、船、河岸”还不够清晰，我们可以加入量词：“三个牧师、三个魔鬼、一艘船、两边河岸”，但还不够，还需要向三个牧师之间有没有区别，以此类推，例如河岸就有起点河岸和重点河岸，它们显然不同，而牧师之间和魔鬼之间，暂时还没有区别，所以最后的我们分析出的对象是：</p>
<p><strong>三个牧师、三个魔鬼、一艘船、起点河岸和终点河岸。</strong></p>
<blockquote>
<p>Tip: 分析游戏对象是在游戏设计前必须做的一件事，因为我们后续设计的模型类、控制器类和它们密切相关，如果没有好好去分析对象、分析对象数目、分析同类对象之间有没有差别，在之后的设计中可能会导致混乱。因此，分析对象可以用一个简单的三步走方法：“有什么对象” - “对象各有几个” - “同类对象之间有没有差别”。</p>
</blockquote>
<h3 id="第二步分析玩家游戏行为">第二步：分析玩家游戏行为</h3>
<p>游戏行为与游戏规则密切相关，但往往一个游戏中游戏行为不会很多，我们可以使用一个表列出来。在我们分析好游戏行为后，将使用Fasade门面模式来实现交互行为的设计。</p>
<p>我们可以发现，牧师与魔鬼过河中的游戏行为有四个：点击牧师、点击魔鬼、点击船、重新开始游戏，我们发现点击牧师和点击魔鬼可以用点击角色来替换，因为二者的游戏行为本质上没有差别，再加上游戏行为少往往能让代码更加简短清晰，随意我们最终总结出三个游戏行为，如下表格所示：</p>
<table>
<thead>
<tr>
<th>动作</th>
<th>参数</th>
<th>结果</th>
</tr>
</thead>
<tbody>
<tr>
<td>重启游戏 gameRestart</td>
<td></td>
<td>所有游戏对象回到初始状态</td>
</tr>
<tr>
<td>点击牧师/魔鬼 clickCharacter</td>
<td>指定角色</td>
<td>若在岸上则上船，若在船上则上岸</td>
</tr>
<tr>
<td>点击船 clickBoat</td>
<td></td>
<td>在有人在船上的前提下，移动到对岸</td>
</tr>
</tbody>
</table>
<h3 id="第三步实现">第三步：实现</h3>
<p>我们使用MVC架构实现游戏。以下是MVC架构的UML类图模板，以GUI为后缀的类属于视图层、以Controller为后缀属于控制器层，以Model为后缀的类属于模型层，在接下来的类的设计中我们也遵循这样的命名规则。</p>
<p><img src="../../images/unity3d2/UML2.png" alt="uml2"></p>
<p>我们从导演类开始，一步一步向下设计。</p>
<h4 id="director类">Director类</h4>
<p>Director类负责“指挥”整个游戏，一个游戏中当然不应该出现多个导演，因此我们使用单例模式来保证导演全局唯一。</p>
<p>导演“手握”某个场景的控制器，但是由于游戏可能有多个场景，不同场景往往使用不同的控制器。我们不能指定某个具体的控制器类给导演，但是我们可以定义一个所有所有控制器都继承的类或者接口，所有控制器类都需要继承该类/接口，这样导演手中的控制器就可以“自由切换”，无论它们的实现类是代码如下。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">Director</span> : System.Object
{
    <span style="color:#228b22">// Singlton
</span><span style="color:#228b22"></span>    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#8b008b;font-weight:bold">static</span> Director _instance;
    <span style="color:#8b008b;font-weight:bold">public</span> ISceneController currentSceneController{ <span style="color:#8b008b;font-weight:bold">get</span>; <span style="color:#8b008b;font-weight:bold">set</span>; }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> Director getInstance(){
        <span style="color:#8b008b;font-weight:bold">if</span>(_instance == <span style="color:#8b008b;font-weight:bold">null</span>){
            _instance = <span style="color:#8b008b;font-weight:bold">new</span> Director();
        }
        <span style="color:#8b008b;font-weight:bold">return</span> _instance;
    }
}
</code></pre></div><h4 id="iscenecontroller接口">ISceneController接口</h4>
<p>ISceneController就是Direstor所持有的类型，作为借口，它所有的方法是所有控制器类待具体实现的，在这个牧师与魔鬼过河中，我们的场景只需要“加载资源”，因此该接口只规定了一个方法。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">interface</span> ISceneController{
    <span style="color:#8b008b;font-weight:bold">void</span> loadResources();
}
</code></pre></div><h4 id="iuseraction">IUserAction</h4>
<p>直接上代码，定义了我们第二步时分析的三个用户行为动作，这里的参数是我们接下要定义的角色模型和船模型。同样的，控制器需要继承这个接口并实现三个方法。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#">    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">interface</span> IUserAction{
    <span style="color:#8b008b;font-weight:bold">void</span> gameRestart();
    <span style="color:#8b008b;font-weight:bold">void</span> clickCharacter(CharacterModel characterModel);
    <span style="color:#8b008b;font-weight:bold">void</span> clickBoat(BoatModel boatModel);
}
</code></pre></div><p><strong>接下来很容易想到，我们的去下一个目标是是实现控制器，但在这里我们先把控制器放在一边。因为它需要操纵所有的视图类和模型类，且视图类需要模型类。因此我们的实现顺序应该是先实现模型类，再实现视图类，最后实现控制器。</strong></p>
<h4 id="moveable类">Moveable类</h4>
<p>Moveable类是一个组件类，为模型提供移动的方法。在创建游戏对象是，我们会将Moveable作为组件通过AddComponment函数加入到游戏对象中，这样我们就可以通过调用Moveable类的函数使得游戏对象进行移动。</p>
<p>我们定义了若干个状态，每种状态在之后处理各个动作的交互时有奇效。例如：我们可以通过规定角色运动在SHIPING状态时角色不处理用户点击时间。</p>
<p>在接下来的及各类中我们回频繁使用这样的静态参数，这样可以在类的外部直接调用，并易于理解。</p>
<ul>
<li>WAITING：等待</li>
<li>EMBARK：上船</li>
<li>SHIPING：乘船移动</li>
<li>DISEMBARK：下船</li>
</ul>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">System.Collections</span>;
<span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">System.Collections.Generic</span>;
<span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">UnityEngine</span>;

<span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">Moveable</span>: MonoBehaviour{

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> WAITING = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> EMBARK = <span style="color:#b452cd">1</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> SHIPING = <span style="color:#b452cd">2</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> DISEMBARK = <span style="color:#b452cd">3</span>;

    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#8b008b;font-weight:bold">const</span> <span style="color:#00688b;font-weight:bold">float</span> SPEED = <span style="color:#b452cd">0.1F</span>;
    <span style="color:#8b008b;font-weight:bold">private</span> Vector3 destPosition;
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">int</span> currentState;

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> Update(){
        <span style="color:#8b008b;font-weight:bold">if</span>(currentState != WAITING &amp;&amp; transform.position != destPosition){
            transform.position = Vector3.MoveTowards(transform.position, destPosition, SPEED);
        }
        <span style="color:#8b008b;font-weight:bold">else</span>{
            currentState = WAITING;
        }
    }


    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setDestPosition(Vector3 destPosition){
        <span style="color:#8b008b;font-weight:bold">this</span>.destPosition = destPosition;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setCurrentState(<span style="color:#00688b;font-weight:bold">int</span> currentState){
        <span style="color:#8b008b;font-weight:bold">this</span>.currentState = currentState;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getCurrentState(){
        <span style="color:#8b008b;font-weight:bold">return</span> <span style="color:#8b008b;font-weight:bold">this</span>.currentState;
    }
    
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> resetMoveable(){
        currentState = WAITING;
    }
}
</code></pre></div><h4 id="charactermodel">CharacterModel</h4>
<p>Character通过它的类别CharacterType区分是牧师（CharacterModel.PRIEST）还是魔鬼（CharacterModel.DEVIL），通过它的状态CharacterState来区分是在起点岸上（CharacterModel.ASHORE_START），还是在终点岸上（CharacterModel.ASHROE_DESTINATION），还是在船上（CharacterModel.ONBOARD）。</p>
<p>一个CharacterModel包含GameObject对象，起始位置和Moveable对象，我们将给每一个牧师、恶魔都分配一个CharacterModel。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">System.Collections</span>;
<span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">System.Collections.Generic</span>;
<span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">UnityEngine</span>;

<span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">CharacterModel</span>{
    <span style="color:#8b008b;font-weight:bold">private</span> GameObject character;
    <span style="color:#8b008b;font-weight:bold">private</span> CharacterGUI characterGUI;
    <span style="color:#8b008b;font-weight:bold">private</span> Moveable moveable; 
    <span style="color:#8b008b;font-weight:bold">private</span> Vector3 initialPosition;

    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">int</span> characterType;
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">int</span> characterState;

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> PRIEST = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> DEVIL = <span style="color:#b452cd">1</span>; 
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> ASHORE_START = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> ASHORE_DESTINATION = <span style="color:#b452cd">1</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> ONBOARD = <span style="color:#b452cd">2</span>;

    <span style="color:#8b008b;font-weight:bold">public</span> CharacterModel(<span style="color:#00688b;font-weight:bold">int</span> characterType, <span style="color:#00688b;font-weight:bold">string</span> characterName, Vector3 position){
        <span style="color:#8b008b;font-weight:bold">this</span>.characterType = characterType;
        <span style="color:#8b008b;font-weight:bold">this</span>.characterState = ASHORE_START;
        <span style="color:#8b008b;font-weight:bold">this</span>.initialPosition = position;

        <span style="color:#8b008b;font-weight:bold">if</span>(characterType == PRIEST){
            character = Object.Instantiate (Resources.Load (<span style="color:#cd5555">&#34;Prefabs/Priest&#34;</span>, <span style="color:#8b008b;font-weight:bold">typeof</span>(GameObject)), Vector3.zero, Quaternion.identity, <span style="color:#8b008b;font-weight:bold">null</span>) <span style="color:#8b008b;font-weight:bold">as</span> GameObject;
        }
        <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(characterType == DEVIL){
            character = Object.Instantiate (Resources.Load (<span style="color:#cd5555">&#34;Prefabs/Devil&#34;</span>, <span style="color:#8b008b;font-weight:bold">typeof</span>(GameObject)), Vector3.zero, Quaternion.identity, <span style="color:#8b008b;font-weight:bold">null</span>) <span style="color:#8b008b;font-weight:bold">as</span> GameObject;
        }

        character.name = characterName;  
        character.transform.position = position;
        moveable = character.AddComponent (<span style="color:#8b008b;font-weight:bold">typeof</span>(Moveable)) <span style="color:#8b008b;font-weight:bold">as</span> Moveable;
        characterGUI = character.AddComponent (<span style="color:#8b008b;font-weight:bold">typeof</span>(CharacterGUI)) <span style="color:#8b008b;font-weight:bold">as</span> CharacterGUI;
        characterGUI.SetCharacterGUI(UserGUI.CHARACTER, <span style="color:#8b008b;font-weight:bold">this</span>);
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getCharacterType(){
        <span style="color:#8b008b;font-weight:bold">return</span> characterType;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getCharacterState(){
        <span style="color:#8b008b;font-weight:bold">return</span> characterState;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> Vector3 getInitialPosition(){
        <span style="color:#8b008b;font-weight:bold">return</span> initialPosition;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> GameObject getGameObject(){
        <span style="color:#8b008b;font-weight:bold">return</span> character;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setCharacterState(<span style="color:#00688b;font-weight:bold">int</span> characterState){
        <span style="color:#8b008b;font-weight:bold">this</span>.characterState = characterState;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setCharacterPosition(Vector3 position){
        character.transform.position = position;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> moveCharacterPosition(Vector3 position, <span style="color:#00688b;font-weight:bold">int</span> state){
        moveable.setDestPosition(position);
        moveable.setCurrentState(state);
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> resetCharacter(){
        moveable.resetMoveable();
        characterState = ASHORE_START;
        setCharacterPosition(initialPosition);
    }
}
</code></pre></div><h4 id="boatmodel">BoatModel</h4>
<p>BoatModel比CharacterModel稍微复杂一些，因为它需要载人。</p>
<p>除了游戏对象，位置信息和Moveable对象，BoatModel对象中还包含座位数组sits和Character对象数组，用与记录当前上船的人。</p>
<p>SHIPING、PARKING_DESTINATION、PARKING_START分别代表船的三种状态：行驶中、停泊在终点河岸、停泊在起点河岸。</p>
<p>BoatModel对象提供函数<code>getEmptySitsCount()</code>来获取有当前船上空位置个数，提供函数<code>getOccupiedSitsCount()</code>来获取船上的人数；提供<code>getASits()</code>和<code>leaveASit()</code>来模拟角色上船和下船。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">BoatModel</span>
{
    <span style="color:#8b008b;font-weight:bold">private</span> GameObject boat;
    <span style="color:#8b008b;font-weight:bold">private</span> BoatGUI boatGUI;
    <span style="color:#8b008b;font-weight:bold">private</span> Moveable moveable; 
    <span style="color:#8b008b;font-weight:bold">private</span> Vector3 startPosition, destinationPosition;

    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">int</span> boatState;
    
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">int</span>[] sits = <span style="color:#8b008b;font-weight:bold">new</span> <span style="color:#00688b;font-weight:bold">int</span>[<span style="color:#b452cd">2</span>];
    <span style="color:#8b008b;font-weight:bold">private</span> CharacterModel[] characterModels = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel[<span style="color:#b452cd">2</span>];
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> EMPTY_SIT = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> OCCUPIED_SIT = <span style="color:#b452cd">1</span>;

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> SHIPING = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> PARKING_DESTINATION = <span style="color:#b452cd">1</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> PARKING_START = <span style="color:#b452cd">2</span>;

    <span style="color:#8b008b;font-weight:bold">public</span> BoatModel(Vector3 startPosition, Vector3 destinationPosition){
        <span style="color:#8b008b;font-weight:bold">this</span>.boatState = PARKING_START;

        boat = Object.Instantiate &lt;GameObject&gt; (Resources.Load &lt;GameObject&gt; (<span style="color:#cd5555">&#34;Prefabs/Boat&#34;</span>), Vector3.zero, Quaternion.identity);
        boat.name = <span style="color:#cd5555">&#34;Boat&#34;</span>;

        boat.transform.position = startPosition;
        <span style="color:#8b008b;font-weight:bold">this</span>.startPosition = startPosition;
        <span style="color:#8b008b;font-weight:bold">this</span>.destinationPosition = destinationPosition;
        moveable = boat.AddComponent (<span style="color:#8b008b;font-weight:bold">typeof</span>(Moveable)) <span style="color:#8b008b;font-weight:bold">as</span> Moveable;
        boatGUI = boat.AddComponent (<span style="color:#8b008b;font-weight:bold">typeof</span>(BoatGUI)) <span style="color:#8b008b;font-weight:bold">as</span> BoatGUI;
        boatGUI.SetBoatGUI(UserGUI.BOAT, <span style="color:#8b008b;font-weight:bold">this</span>);

        sits[<span style="color:#b452cd">0</span>] = sits[<span style="color:#b452cd">1</span>] = EMPTY_SIT;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getOccupiedSitsCount(){
        <span style="color:#00688b;font-weight:bold">int</span> num = <span style="color:#b452cd">0</span>;
        <span style="color:#8b008b;font-weight:bold">for</span>(<span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">2</span>; i++){
            <span style="color:#8b008b;font-weight:bold">if</span>(sits[i] == OCCUPIED_SIT){
                num++;
            }
        }
        <span style="color:#8b008b;font-weight:bold">return</span> num;
    }
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getEmptySitsCount(){
        <span style="color:#8b008b;font-weight:bold">return</span> <span style="color:#b452cd">2</span> - getOccupiedSitsCount();
    }

    <span style="color:#8b008b;font-weight:bold">public</span> Vector3 getASit(CharacterModel characterModel){
        <span style="color:#8b008b;font-weight:bold">for</span>(<span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">2</span>; i++){
            <span style="color:#8b008b;font-weight:bold">if</span>(sits[i] == EMPTY_SIT){
                characterModel.getGameObject().transform.parent = boat.transform;
                sits[i] = OCCUPIED_SIT;
                characterModels[i] = characterModel;
                <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterState() == CharacterModel.ASHORE_START){
                    <span style="color:#8b008b;font-weight:bold">return</span> getStartPosition() - Vector3.right * <span style="color:#b452cd">0.6F</span> + Vector3.right * <span style="color:#b452cd">1.2F</span> * i;
                }
                <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterState() == CharacterModel.ASHORE_DESTINATION){
                    <span style="color:#8b008b;font-weight:bold">return</span> getDestinationPosition() - Vector3.right * <span style="color:#b452cd">0.6F</span> + Vector3.right * <span style="color:#b452cd">1.2F</span> * i;
                }
               
            }
        }
        <span style="color:#8b008b;font-weight:bold">return</span> characterModel.getInitialPosition();
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> leaveASit(CharacterModel characterModel){
        <span style="color:#8b008b;font-weight:bold">for</span>(<span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">2</span>; i++){
            <span style="color:#8b008b;font-weight:bold">if</span>(characterModels[i] == characterModel){
                sits[i] = EMPTY_SIT;
                characterModel.getGameObject().transform.parent = <span style="color:#8b008b;font-weight:bold">null</span>;
                characterModels[i] = <span style="color:#8b008b;font-weight:bold">null</span>;
            }
        }
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setBoatState(<span style="color:#00688b;font-weight:bold">int</span> boatState){
        <span style="color:#8b008b;font-weight:bold">this</span>.boatState = boatState;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getBoatState(){
        <span style="color:#8b008b;font-weight:bold">return</span> boatState;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setBoatPosition(Vector3 position){
        boat.transform.position = position;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> Vector3 getStartPosition(){
        <span style="color:#8b008b;font-weight:bold">return</span> startPosition;
    }
    <span style="color:#8b008b;font-weight:bold">public</span> Vector3 getDestinationPosition(){
        <span style="color:#8b008b;font-weight:bold">return</span> destinationPosition;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">int</span> getMoveableCurrentState(){
        <span style="color:#8b008b;font-weight:bold">return</span> moveable.getCurrentState();
    }

    <span style="color:#8b008b;font-weight:bold">public</span> GameObject getGameObject(){
        <span style="color:#8b008b;font-weight:bold">return</span> boat;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> moveBoatPosition(Vector3 position, <span style="color:#00688b;font-weight:bold">int</span> state){
        moveable.setDestPosition(position);
        moveable.setCurrentState(state);
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> resetBoat(){
        moveable.resetMoveable();
        boatState = PARKING_START;
        setBoatPosition(startPosition);
        <span style="color:#8b008b;font-weight:bold">for</span>( <span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">2</span>; i++){
            sits[i] = EMPTY_SIT;
            <span style="color:#8b008b;font-weight:bold">if</span>(characterModels[i] != <span style="color:#8b008b;font-weight:bold">null</span>){
                characterModels[i].getGameObject().transform.parent = <span style="color:#8b008b;font-weight:bold">null</span>;
                characterModels[i] = <span style="color:#8b008b;font-weight:bold">null</span>;
            }
        }
    }
    
}
</code></pre></div><p><strong>以上是三个业务模型类，接下来分析视图层</strong></p>
<h4 id="usergui">UserGUI</h4>
<p>UserGUI是一个抽象类，可以被BoatGUI和CharacterGUI继承，分别实现不同的OnMouseDown点击事件。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">abstract</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">UserGUI</span>: MonoBehaviour{
    <span style="color:#8b008b;font-weight:bold">protected</span> IUserAction userAction;
    <span style="color:#8b008b;font-weight:bold">protected</span> <span style="color:#00688b;font-weight:bold">int</span> clickedObjectType;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> CHARACTER = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> BOAT = <span style="color:#b452cd">1</span>;

    <span style="color:#8b008b;font-weight:bold">void</span> Start(){
        userAction = Director.getInstance().currentSceneController <span style="color:#8b008b;font-weight:bold">as</span> IUserAction;
    }

}
</code></pre></div><h4 id="boatgui">BoatGUI</h4>
<p>直接看代码：</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">BoatGUI</span> : UserGUI
{
    <span style="color:#8b008b;font-weight:bold">private</span> BoatModel boatModel;

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> SetBoatGUI(<span style="color:#00688b;font-weight:bold">int</span> clickedObjectType, BoatModel boatModel){
        <span style="color:#8b008b;font-weight:bold">this</span>.clickedObjectType = clickedObjectType;
        <span style="color:#8b008b;font-weight:bold">if</span>(clickedObjectType == BOAT){
            <span style="color:#8b008b;font-weight:bold">this</span>.boatModel = boatModel;
        }
    }

    <span style="color:#8b008b;font-weight:bold">void</span> OnMouseDown(){
        userAction.clickBoat(boatModel);    
    }
}
</code></pre></div><h4 id="charactergui">CharacterGUI</h4>
<p>和BoatGUI类似。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">CharacterGUI</span> : UserGUI
{
    <span style="color:#8b008b;font-weight:bold">private</span> CharacterModel characterModel;

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> SetCharacterGUI(<span style="color:#00688b;font-weight:bold">int</span> clickedObjectType, CharacterModel characterModel){
        <span style="color:#8b008b;font-weight:bold">this</span>.clickedObjectType = clickedObjectType;
        <span style="color:#8b008b;font-weight:bold">if</span>(clickedObjectType == CHARACTER){
            <span style="color:#8b008b;font-weight:bold">this</span>.characterModel = characterModel;
        }
    }

    <span style="color:#8b008b;font-weight:bold">void</span> OnMouseDown(){
        userAction.clickCharacter(characterModel);
    }
}
</code></pre></div><h4 id="scenegui">SceneGUI</h4>
<p>SceneGUI不处理游戏对象的点击事件，而是处理游戏之外的逻辑，包括定义GUISkin、显示Gamover、You win和重新开始按钮。</p>
<p>SceneGUI需要获得游戏输赢(gameState)，并以静态变量表示：UNKONWN、WIN、LOSE，而unlock用于锁定游戏状态。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">SceneGUI</span> : MonoBehaviour
{
    <span style="color:#8b008b;font-weight:bold">private</span> IUserAction userAction;
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">bool</span> unlock;
    <span style="color:#8b008b;font-weight:bold">private</span> <span style="color:#00688b;font-weight:bold">int</span> gameState;

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> UNKNOWN = <span style="color:#b452cd">0</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> WIN = <span style="color:#b452cd">1</span>;
    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">static</span> <span style="color:#00688b;font-weight:bold">int</span> LOSE = <span style="color:#b452cd">2</span>;

    <span style="color:#8b008b;font-weight:bold">void</span> Start(){
        unlock = <span style="color:#8b008b;font-weight:bold">true</span>;
        gameState = <span style="color:#b452cd">0</span>;
        userAction = Director.getInstance().currentSceneController <span style="color:#8b008b;font-weight:bold">as</span> IUserAction;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> setGameState(<span style="color:#00688b;font-weight:bold">int</span> state){
        gameState = state;
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#00688b;font-weight:bold">bool</span> getUnlock(){
        <span style="color:#8b008b;font-weight:bold">return</span> unlock;
    }

    <span style="color:#8b008b;font-weight:bold">void</span> OnGUI(){
        GUI.skin = Resources.Load &lt;GUISkin&gt; (<span style="color:#cd5555">&#34;Skin/MySkin&#34;</span>);

        <span style="color:#228b22">// Restart Button
</span><span style="color:#228b22"></span>        <span style="color:#8b008b;font-weight:bold">if</span>(GUI.Button(<span style="color:#8b008b;font-weight:bold">new</span> Rect(Screen.width - <span style="color:#b452cd">50</span>, <span style="color:#b452cd">0</span>, <span style="color:#b452cd">50</span>, <span style="color:#b452cd">50</span>), <span style="color:#cd5555">&#34; &#34;</span>)) {
            setGameState(UNKNOWN);
            userAction.gameRestart();
            unlock = <span style="color:#8b008b;font-weight:bold">true</span>;
        }

        <span style="color:#8b008b;font-weight:bold">if</span>(gameState == WIN){
            unlock = <span style="color:#8b008b;font-weight:bold">false</span>;
            GUI.Label(<span style="color:#8b008b;font-weight:bold">new</span> Rect(Screen.width / <span style="color:#b452cd">2</span> - <span style="color:#b452cd">200</span>, Screen.height / <span style="color:#b452cd">2</span> - <span style="color:#b452cd">100</span>, <span style="color:#b452cd">400</span>, <span style="color:#b452cd">200</span>), Resources.Load &lt;Texture2D&gt; (<span style="color:#cd5555">&#34;Skin/Win&#34;</span>));
        }
        <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(gameState == LOSE){
            unlock = <span style="color:#8b008b;font-weight:bold">false</span>;
            GUI.Label(<span style="color:#8b008b;font-weight:bold">new</span> Rect(Screen.width / <span style="color:#b452cd">2</span> - <span style="color:#b452cd">200</span>, Screen.height / <span style="color:#b452cd">2</span> - <span style="color:#b452cd">100</span>, <span style="color:#b452cd">400</span>, <span style="color:#b452cd">200</span>), Resources.Load &lt;Texture2D&gt; (<span style="color:#cd5555">&#34;Skin/Gameover&#34;</span>));
            
        }
    }
}

</code></pre></div><h4 id="mainscenecontroller">MainSceneController</h4>
<p>最后是控制器，控制器实现ISceneController和IUserAction，操控所有的游戏对象、模型和GUI。</p>
<p>控制器中不仅实现了游戏行为，还实现了游戏胜负判定（gameJudge()）,和简单游戏对象的生成（generateGameObject()）。</p>
<p>虽然代码量比其它类长，但是它各个函数的功能清晰明了。</p>
<div class="highlight"><pre style="background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c#" data-lang="c#"><span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">System.Collections</span>;
<span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">System.Collections.Generic</span>;
<span style="color:#8b008b;font-weight:bold">using</span> <span style="color:#008b45;text-decoration:underline">UnityEngine</span>;

<span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">class</span> <span style="color:#008b45;font-weight:bold">MainSceneController</span> : MonoBehaviour, ISceneController, IUserAction
{
    
    Vector3 riverPosition = <span style="color:#8b008b;font-weight:bold">new</span> Vector3(<span style="color:#b452cd">0</span>,<span style="color:#b452cd">0</span>,<span style="color:#b452cd">0</span>); <span style="color:#228b22">// 10 X 10 X 1
</span><span style="color:#228b22"></span>    Vector3 leftBankPosition = <span style="color:#8b008b;font-weight:bold">new</span> Vector3(-<span style="color:#b452cd">35</span>,<span style="color:#b452cd">0</span>,<span style="color:#b452cd">0</span>); <span style="color:#228b22">// 10 X 10 X 1.2
</span><span style="color:#228b22"></span>    Vector3 rightBankPosition = <span style="color:#8b008b;font-weight:bold">new</span> Vector3(<span style="color:#b452cd">35</span>,<span style="color:#b452cd">0</span>,<span style="color:#b452cd">0</span>); <span style="color:#228b22">// 10 X 10 X 1.2
</span><span style="color:#228b22"></span>    Vector3 boatStartPosition = <span style="color:#8b008b;font-weight:bold">new</span> Vector3(<span style="color:#b452cd">5</span>,<span style="color:#b452cd">0</span>,<span style="color:#b452cd">0</span>); <span style="color:#228b22">// 2 X 4 X 0.5
</span><span style="color:#228b22"></span>    Vector3 boatDestinationPosition = <span style="color:#8b008b;font-weight:bold">new</span> Vector3(-<span style="color:#b452cd">5</span>,<span style="color:#b452cd">0</span>,<span style="color:#b452cd">0</span>); 
    Vector3[] characterInitPosition = <span style="color:#8b008b;font-weight:bold">new</span> Vector3[<span style="color:#b452cd">6</span>];

    GameObject river, leftBank, rightBank;
    BoatModel boatModel;
    CharacterModel[] priests = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel[<span style="color:#b452cd">3</span>];
    CharacterModel[] devils = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel[<span style="color:#b452cd">3</span>];

    SceneGUI sceneGUI;

    <span style="color:#8b008b;font-weight:bold">void</span> Awake(){
        Debug.Log(<span style="color:#cd5555">&#34;Preparing...&#34;</span>);
        Director director = Director.getInstance();
        director.currentSceneController = <span style="color:#8b008b;font-weight:bold">this</span>;
        director.currentSceneController.loadResources();
        sceneGUI = gameObject.AddComponent (<span style="color:#8b008b;font-weight:bold">typeof</span>(SceneGUI)) <span style="color:#8b008b;font-weight:bold">as</span> SceneGUI;
    }

    <span style="color:#8b008b;font-weight:bold">void</span> generateGameObjects(){
        <span style="color:#228b22">// River
</span><span style="color:#228b22"></span>        river = Instantiate &lt;GameObject&gt; (Resources.Load &lt;GameObject&gt; (<span style="color:#cd5555">&#34;Prefabs/River&#34;</span>), riverPosition, Quaternion.identity);
        river.name = <span style="color:#cd5555">&#34;River&#34;</span>;

        <span style="color:#228b22">// The Bank
</span><span style="color:#228b22"></span>        leftBank = Instantiate &lt;GameObject&gt; (Resources.Load &lt;GameObject&gt; (<span style="color:#cd5555">&#34;Prefabs/RiverBank&#34;</span>), leftBankPosition, Quaternion.identity);
		leftBank.name = <span style="color:#cd5555">&#34;DestBank&#34;</span>;
        rightBank = Instantiate &lt;GameObject&gt; (Resources.Load &lt;GameObject&gt; (<span style="color:#cd5555">&#34;Prefabs/RiverBank&#34;</span>), rightBankPosition, Quaternion.identity);
		rightBank.name = <span style="color:#cd5555">&#34;StartBank&#34;</span>;
        
        <span style="color:#228b22">// Boat
</span><span style="color:#228b22"></span>        boatModel = <span style="color:#8b008b;font-weight:bold">new</span> BoatModel(boatStartPosition, boatDestinationPosition);

        <span style="color:#228b22">// Priests &amp; Devils
</span><span style="color:#228b22"></span>        <span style="color:#8b008b;font-weight:bold">for</span>(<span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">6</span>; i++){
            characterInitPosition[i] = rightBankPosition + Vector3.right * (i * <span style="color:#b452cd">1.2F</span> - <span style="color:#b452cd">2</span> - <span style="color:#b452cd">25</span>);
        }

        priests[<span style="color:#b452cd">0</span>] = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel(CharacterModel.PRIEST, <span style="color:#cd5555">&#34;Justica&#34;</span>, characterInitPosition[<span style="color:#b452cd">0</span>]);
        priests[<span style="color:#b452cd">1</span>] = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel(CharacterModel.PRIEST, <span style="color:#cd5555">&#34;Kindmes&#34;</span>, characterInitPosition[<span style="color:#b452cd">1</span>]);
        priests[<span style="color:#b452cd">2</span>] = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel(CharacterModel.PRIEST, <span style="color:#cd5555">&#34;Loyalti&#34;</span>, characterInitPosition[<span style="color:#b452cd">2</span>]);

        devils[<span style="color:#b452cd">0</span>] = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel(CharacterModel.DEVIL, <span style="color:#cd5555">&#34;Gred&#34;</span>, characterInitPosition[<span style="color:#b452cd">3</span>]);
        devils[<span style="color:#b452cd">1</span>] = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel(CharacterModel.DEVIL, <span style="color:#cd5555">&#34;Arroganca&#34;</span>, characterInitPosition[<span style="color:#b452cd">4</span>]);
        devils[<span style="color:#b452cd">2</span>] = <span style="color:#8b008b;font-weight:bold">new</span> CharacterModel(CharacterModel.DEVIL, <span style="color:#cd5555">&#34;Lazi&#34;</span>, characterInitPosition[<span style="color:#b452cd">5</span>]);
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> loadResources(){
        Debug.Log(<span style="color:#cd5555">&#34;Loading resources...&#34;</span>);
        generateGameObjects();
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> gameRestart(){
        Debug.Log(<span style="color:#cd5555">&#34;Restart game...&#34;</span>);
        boatModel.resetBoat();
        <span style="color:#8b008b;font-weight:bold">for</span>(<span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">3</span>; i++){
            priests[i].resetCharacter();
            devils[i].resetCharacter();
        }
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> clickCharacter(CharacterModel characterModel){
        <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterType() == CharacterModel.PRIEST){
            Debug.Log(<span style="color:#cd5555">&#34;Priest clicked&#34;</span>);
        }
        <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterType() == CharacterModel.DEVIL){
            Debug.Log(<span style="color:#cd5555">&#34;Devil clicked&#34;</span>);
        }

        <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterState() == CharacterModel.ASHORE_START &amp;&amp; boatModel.getBoatState() == BoatModel.PARKING_START){
            <span style="color:#228b22">// StartBank 上船
</span><span style="color:#228b22"></span>            <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getEmptySitsCount() != <span style="color:#b452cd">0</span>){
                characterModel.moveCharacterPosition(boatModel.getASit(characterModel), Moveable.EMBARK);
                characterModel.setCharacterState(CharacterModel.ONBOARD);
            } 
        }
        <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterState() == CharacterModel.ASHORE_DESTINATION &amp;&amp; boatModel.getBoatState() == BoatModel.PARKING_DESTINATION){
            <span style="color:#228b22">// DestinationBank 上船
</span><span style="color:#228b22"></span>            <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getEmptySitsCount() != <span style="color:#b452cd">0</span>){
                characterModel.moveCharacterPosition(boatModel.getASit(characterModel), Moveable.EMBARK);
                characterModel.setCharacterState(CharacterModel.ONBOARD);
            }
        }
        <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(characterModel.getCharacterState() == CharacterModel.ONBOARD){
            <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getBoatState() == BoatModel.PARKING_START){ 
                <span style="color:#228b22">// StartBank 下船
</span><span style="color:#228b22"></span>                boatModel.leaveASit(characterModel);
                characterModel.moveCharacterPosition(characterModel.getInitialPosition(), Moveable.DISEMBARK);
                characterModel.setCharacterState(CharacterModel.ASHORE_START);

            }
            <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getBoatState() == BoatModel.PARKING_DESTINATION){
                <span style="color:#228b22">// DestinationBank 下船
</span><span style="color:#228b22"></span>                boatModel.leaveASit(characterModel);
                characterModel.moveCharacterPosition(characterModel.getInitialPosition() - Vector3.right * <span style="color:#b452cd">2</span> * characterModel.getInitialPosition().x, Moveable.DISEMBARK);
                characterModel.setCharacterState(CharacterModel.ASHORE_DESTINATION);
            }
        }

    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> clickBoat(BoatModel boatModel){
        Debug.Log(<span style="color:#cd5555">&#34;Boat clicked&#34;</span>);
        <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getOccupiedSitsCount() != <span style="color:#b452cd">0</span>){
            <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getBoatState() == BoatModel.PARKING_START){
                <span style="color:#228b22">// StartBank -&gt; DestinationBank
</span><span style="color:#228b22"></span>                boatModel.moveBoatPosition(boatModel.getDestinationPosition(), Moveable.SHIPING);
                boatModel.setBoatState(BoatModel.PARKING_DESTINATION);
            }
            <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getBoatState() == BoatModel.PARKING_DESTINATION){
                <span style="color:#228b22">// DestinationBank -&gt; StartBank
</span><span style="color:#228b22"></span>                boatModel.moveBoatPosition(boatModel.getStartPosition(), Moveable.SHIPING);
                boatModel.setBoatState(BoatModel.PARKING_START);
            }
        }
    }

    <span style="color:#8b008b;font-weight:bold">void</span> Update(){
        <span style="color:#8b008b;font-weight:bold">if</span>(sceneGUI.getUnlock() ==  <span style="color:#8b008b;font-weight:bold">true</span>){
            gameJudge();
        }
    }

    <span style="color:#8b008b;font-weight:bold">public</span> <span style="color:#8b008b;font-weight:bold">void</span> gameJudge(){
        <span style="color:#00688b;font-weight:bold">int</span> startDevilNum = <span style="color:#b452cd">0</span>, destinationDevilNum = <span style="color:#b452cd">0</span>;
        <span style="color:#00688b;font-weight:bold">int</span> startPriestNum = <span style="color:#b452cd">0</span>, destinationPriestNum = <span style="color:#b452cd">0</span>;

        <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getMoveableCurrentState() == Moveable.SHIPING){
            <span style="color:#8b008b;font-weight:bold">return</span>;
        }

        <span style="color:#8b008b;font-weight:bold">for</span>(<span style="color:#00688b;font-weight:bold">int</span> i = <span style="color:#b452cd">0</span>; i &lt; <span style="color:#b452cd">3</span>; i++){
            <span style="color:#8b008b;font-weight:bold">if</span>(priests[i].getCharacterState() == CharacterModel.ASHORE_START){
                startPriestNum++;
            }
            <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(priests[i].getCharacterState() == CharacterModel.ASHORE_DESTINATION){
                destinationPriestNum++;
            }

            <span style="color:#8b008b;font-weight:bold">if</span>(devils[i].getCharacterState() == CharacterModel.ASHORE_START){
                startDevilNum++;
            }
            <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(devils[i].getCharacterState() == CharacterModel.ASHORE_DESTINATION){
                destinationDevilNum++;
            }
        }

        <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getOccupiedSitsCount() != <span style="color:#b452cd">0</span>){
            <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getBoatState() == BoatModel.PARKING_START){
                startPriestNum = <span style="color:#b452cd">3</span> - destinationPriestNum;
                startDevilNum = <span style="color:#b452cd">3</span> - destinationDevilNum;
            }
            <span style="color:#8b008b;font-weight:bold">else</span> <span style="color:#8b008b;font-weight:bold">if</span>(boatModel.getBoatState() == BoatModel.PARKING_DESTINATION){
                destinationPriestNum = <span style="color:#b452cd">3</span> - startPriestNum;
                destinationDevilNum = <span style="color:#b452cd">3</span> - startDevilNum;
            }
        }

        <span style="color:#8b008b;font-weight:bold">if</span>((startPriestNum != <span style="color:#b452cd">0</span> &amp;&amp; startPriestNum &lt; startDevilNum) || (destinationPriestNum != <span style="color:#b452cd">0</span> &amp;&amp; destinationPriestNum &lt; destinationDevilNum)){
            Debug.Log(<span style="color:#cd5555">&#34;Game Over&#34;</span>);
            sceneGUI.setGameState(SceneGUI.LOSE);
        }

        <span style="color:#8b008b;font-weight:bold">if</span>(destinationPriestNum == <span style="color:#b452cd">3</span> &amp;&amp; destinationDevilNum == <span style="color:#b452cd">3</span>){
            Debug.Log(<span style="color:#cd5555">&#34;You Win&#34;</span>);
            sceneGUI.setGameState(SceneGUI.WIN);
        }

    }
}

</code></pre></div><hr>
<h3 id="uml">UML</h3>
<p>以下是这个小游戏最终的类图，经过了多次改动，蓝色部分是View视图层，紫色部分是Model层，红色部分是控制器层。</p>
<p>最终的设计仍然存在不足，在接下来分析代码中可以看到控制器的职责分配不够合理。</p>
<p><img src="../../images/unity3d2/uml.png" alt="uml"></p>

                    
                    <HR width="100%" id="EOF">
		    <p style="color:#777;">Last modified on 2021-10-07</p>
                    
                </div>
            </div>
            
            
            <nav class="post-pagination">

                
                <a class="newer-posts" href="https://wondrouswisdomcard.gitee.io/posts/unity3d3/">
			Next<br>用Unity3D实现打飞碟游戏
                </a>
                
                
                
                <a class="older-posts" href="https://wondrouswisdomcard.gitee.io/posts/unity3d1/">
			Previous<br>用Unity3D实现一个井字棋
                </a>
                
            </nav>
            <div class="post-comment-wrapper">
                
<div class="comments">
    <vue-disqus shortname="Your disqus short name."></vue-disqus>
</div>
<noscript><noscript>Please activate JavaScript to view comments.</noscript></noscript>
<a href="https://disqus.com/" class="dsq-brlink">Comments powered by <span class="logo-disqus">Disqus</span></a>



<div id="gitalk-container"></div>






            </div>
        </div>
    </div>
</div>

            </div><div id="single-column-footer">
Hugo Theme <a href="https://github.com/amazingrise/hugo-theme-diary">Diary</a> by <a href="https://amazingrise.net">Rise</a>
<br>
Ported from <a href="https://mak1t0.cc/" target="_blank" rel="noreferrer noopener">Makito</a>'s <a href="https://github.com/SumiMakito/hexo-theme-journal/" target="_blank" rel="noreferrer noopener">Journal.</a> <br>
<br>

&copy;
	
	This is a customized copyright.
	
</div>
            </div>
    <script>
let app;

app = new Vue({
    el: '#app',
    data: {
        scrollY: 0,
        navOpacity: 0,
        isDrawerOpen: false,
        mounted: false,
        isDarkMode: false
    },
    methods: {
            sgn(t, x) {
                let k = 1. / (1. - 2 * t);
                if (x <= t) return 0;
                else if (x >= 1 - t) return 1;
                else {
                    return k * (x - t);
                }
            },
            handleScroll() {
                this.scrollY = window.scrollY;
                this.navOpacity = this.sgn(.0, Math.min(1, Math.max(0, window.scrollY / (this.pageHeadHeight() - this.navBarHeight() * 0.8))));
                const {navBar, navBackground, navTitle, extraContainer, streamContainer} = this.$refs;

                if (this.navOpacity >= 1) {
                    navBackground.style.opacity = 1;
                    navTitle.style.opacity = 1;
                } else {
                    navBackground.style.opacity = 0;
                    navTitle.style.opacity = 0;
                }
            },
            handleResize() {
                const {navBar, navBackground, navTitle, extraContainer, streamContainer} = this.$refs;
                extraContainer.style.left = (streamContainer.offsetWidth - extraContainer.offsetWidth) + 'px';
            },
            navBarHeight() {
                return this.$refs.navBar.offsetHeight;
            },
            pageHeadHeight() {
                return this.$refs.pageHead.offsetHeight;
            },
            toggleDrawer() {
                this.isDrawerOpen = !this.isDrawerOpen;
                document.getElementsByTagName('html')[0].style.overflow = this.isDrawerOpen ? 'hidden' : 'unset';
            },
            closeDrawer() {
                this.isDrawerOpen = false;
                document.getElementsByTagName('html')[0].style.overflow = this.isDrawerOpen ? 'hidden' : 'unset';
            },
            toggleDarkMode() {
                this.isDarkMode = !this.isDarkMode;
                if (this.isDarkMode==true){
                    document.cookie = "night=1;path=/";
                    document.body.classList.add("night");
                } else {
                    document.cookie = "night=0;path=/";
                    document.body.classList.remove("night");
                }
            }
    },
    created() {
        window.addEventListener('scroll', this.handleScroll);
        window.addEventListener('resize', this.handleResize);
        window._nonDesktop = function () {
            let check = false;
            (function (a) {
                if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
            })(navigator.userAgent || navigator.vendor || window.opera);
            return check;
        };
        
        var night = document.cookie.replace(/(?:(?:^|.*;\s*)night\s*\=\s*([^;]*).*$)|^.*$/, "$1");
        if (night==""){
            if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
                
            }
        }else{
            
            if (night=="1") {
                this.toggleDarkMode();
            }
        }
    },
    mounted() {
        this.handleScroll();
        this.handleResize();
        this.mounted = true;

        
    },
    destroyed() {
        window.removeEventListener('scroll', this.handleScroll);
        window.removeEventListener('resize', this.handleResize);
    }
});
</script>

<script src="https://wondrouswisdomcard.gitee.io/js/journal.js"></script>
    </body>
</html>
